package net.fdukedom.epicurus.resource;

import net.fdukedom.epicurus.domain.entity.Plugin;
import net.fdukedom.epicurus.domain.entity.Page;
import net.fdukedom.epicurus.domain.entity.ExtensionPoint;
import net.fdukedom.epicurus.tools.logging.Logger;
import net.fdukedom.epicurus.tools.logging.WriterId;

import java.util.*;

import org.apache.struts.action.ActionForward;

/**
 * Dipatches resources, e.g. plugins, pages. 
 *
 * @author Dmitry Y. Kochelaev
 */
public final class ResourceDispatcher {

    /**
     * Instance of class.
     */
    private static final ResourceDispatcher instance = new ResourceDispatcher();

    private Logger logger = Logger.getInstance(WriterId.RESOURCE_DISPATCHER);

    /**
     * Plugins map.
     */
    private final Map<String, Plugin> plugins = new HashMap<String, Plugin>();

    /**
     * Pages map.
     */
    private final Map<String, Page> pages = new HashMap<String, Page>();

    /**
     * Extensions map. List of extension is stored for each extension point.
     */
    private final Map<String, List<ExtensionHandler>> extensions = new HashMap<String, List<ExtensionHandler>>();

    /**
     * Bundles map.
     */
    private final Map<String, ResourceBundle> bundles = new HashMap<String, ResourceBundle>();

    /**
     * Utility class.
     */
    private ResourceDispatcher() {
    }

    /**
     * Returns instance of the class.
     *
     * @return instance of the class
     */
    public static ResourceDispatcher getInstance() {
        return instance;
    }

    /**
     * Registers plugin. It rewrites link to plugin if plugin with such
     * name already exists.
     *
     * @param plugin plugin to be registered
     */
    public void registerPlugin(Plugin plugin) {
        logger.logInfo("Plugin registered: " + plugin);
        plugins.put(plugin.getName(), plugin);
    }

    /**
     * Checks whether plugin is registered.
     *
     * @param name plugin's name
     *
     * @return true if plugin is registered and false otherwise
     */
    public boolean checkForPlugin(String name) {
        return plugins.containsKey(name);
    }

    /**
     * Looks up for plugin by its name.
     *
     * @param name plugin's name
     *
     * @return plugin or <tt>null</tt> if plugin with such name doesn't exist
     */
    public Plugin findPluginByName(String name) {
        return plugins.get(name);
    }

    /**
     * Registers page. It rewrites location if page with such name
     * already exists.
     *
     * @param page page
     */
    public void registerPage(Page page) {
        logger.logInfo("Page registered: " + page);
        pages.put(page.getName(), page);
    }

    /**
     * Registers pages collection. It rewrites location if page with such name
     * already exists.
     *
     * @param pages pages' collection
     */
    public void registerPages(Collection<Page> pages) {
        for (Page page: pages) {
            registerPage(page);
        }
    }

    /**
     * Checks whether page is registered.
     *
     * @param name page's name
     *
     * @return true if page is registered and false otherwise
     */
    public boolean checkForPage(String name) {
        return pages.containsKey(name);
    }

    /**
     * Looks up for page by its name.
     *
     * @param name page's name
     *
     * @return forward to page or <tt>null</tt> if page with such name doesn't exist
     */
    public ActionForward findPageByName(String name) {
        String location = pages.get(name).getLocation();
        return location == null ? null : new ActionForward("name", location, true);
    }

    /**
     * Registers e-point.
     *
     * @param epoint epoint
     */
    public void registerExtensionPoint(ExtensionPoint epoint) {
        logger.logInfo("Extension point registered: " + epoint);
        extensions.put(epoint.getName(), new ArrayList<ExtensionHandler>());
    }

    /**
     * Registers e-point collection.
     *
     * @param epoints epoints' collection
     */
    public void registerExtensionPoints(Collection<ExtensionPoint> epoints) {
        for (ExtensionPoint epoint: epoints) {
            registerExtensionPoint(epoint);
        }
    }

    /**
     * Checks whether e-point is registered.
     *
     * @param name e-point's name
     *
     * @return true if e-point is registered and false otherwise
     */
    public boolean checkForExtensionPoint(String name) {
        return extensions.containsKey(name);
    }


    /**
     * Registers extensionHandler.
     *
     * @param epointName e-point to be registered in
     * @param extensionHandler extensionHandler
     */
    public void registerExtension(String epointName, ExtensionHandler extensionHandler) {
        List<ExtensionHandler> epointExtensions = extensions.get(epointName);
        if (epointExtensions == null) {
            logger.logWarning("Failed to register extensionHandler to " + epointName);
        } else {
            epointExtensions.add(extensionHandler);
            logger.logInfo("ExtensionHandler registered: " + extensionHandler);
        }
    }

    /**
     * Loads list of extension for specified extension point.
     *
     * @param epointName name of e-point
     *
     * @return list of extension for specified extension point
     */
    public List<ExtensionHandler> getExtensions(String epointName) {
        return extensions.get(epointName);
    }

    /**
     * Returns bundle for name.
     *
     * @param name bundle name
     *
     * @return bundle for name
     */
    public ResourceBundle getBundle(String name) {
        ResourceBundle bundle;
        if (bundles.containsKey(name)) {
            bundle = bundles.get(name);
        } else {
            bundle = PropertyResourceBundle.getBundle(name);
            bundles.put(name, bundle);
        }
        return bundle;
    }

    /**
     * Gets value from specified bundle.
     *
     * @param bundleName bundle name
     *
     * @param key key to search for
     *
     * @return value for key
     */
    public String getBundledValue(String bundleName, String key) {
        ResourceBundle bundle = getBundle(bundleName);
        return bundle.getString(key);
    }
}
